#Kõigepealt loeme sisse paketid
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.0.3 ✓ dplyr 1.0.2
## ✓ tidyr 1.1.1 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(tidytext)
Kui eelmine kord vaatasime ainult väga lühidat tekstivormi laulusõnu, siis täna vaatame pikemaid vorme. Näiteks Tõde ja Õigus 1. köide on terves mahus antud siinses ilukirjanduse kollektsioonis https://datadoi.ee/handle/33/76. Need failid on meil hetkel kaasas, loeme selle sisse.
raamat1 <- read_tsv("data/uiboaed_ilukirjandus/soned/Anton_Hansen_Tammsaare_Tode_ja_oigus_I.utf8",col_names = F) %>% rename(txt=X1)
## Parsed with column specification:
## cols(
## X1 = col_character()
## )
Vaatame faili sisse, näeme, et failis on iga rida eraldi peatükk. Sellisel juhul saame me kasutada seda infot peatükkide nummerdamiseks, nii nagu eelmine kord nummerdasime järjekorda ja sõnade asukohta.
raamat1 <- raamat1 %>%
mutate(chapter=row_number())
Tükeldame tekstid sõnadeks unnest_tokens() käsuga paketist tidytext.
raamat1_sonad <- raamat1 %>%
unnest_tokens(word,txt)
Ja vaatame, mis pikkusega meie peatükid on.
raamat1_sonad %>%
count(chapter)
Et infost head ülevaadet saada, tasub tihti teha graafikuid.
raamat1_sonad %>%
count(chapter) %>%
ggplot(aes(x=chapter,y=n))+
geom_col()
Peatükid siin on erineva pikkusega. Sellega peaks arvestama kui võrdleme peatükke teineteisega.
Teeme ka sõnaloendi, vaatame seda.
raamat1_sonaloend <- raamat1_sonad %>%
count(word,sort=T)
Ja teeme sõnaloendi ka peatüki kaupa.
peatykid_sonad <- raamat1_sonad %>%
group_by(chapter) %>%
count(word,sort=T)
Siin peaksime niisiis vaatama sagedust. Kui toornumbri järgi on sõna ‘ta’ peatükis 37 ka top 10-s, siis sagedus on tal selgelt väiksem.
peatykid_sonad <- raamat1_sonad %>%
group_by(chapter) %>%
count(word,sort=T) %>%
mutate(sagedus=n/sum(n))
peatykid_sonad
Veel kasutasime eelmine kord käsku mutate() uute tulpade tegemiseks, ja row_number() ja n() ridade järjestuse ja arvu saamiseks. Selle põhjal saab välja arvutada sõna asukoha protsentides, jagades järjekorranumbri koguarvuga.
asukohad <- raamat1_sonad %>%
mutate(nr=row_number(), n=n()) %>%
mutate(asukoht=nr/(n+1)) %>%
ungroup()
Ja nende seast saab kasutada juba filter() käsku, et leida meile huvitavaid sõnu. Näiteks võtame raamatu põhiteemad “töö” ja “armastus”.
asukohad %>%
filter(word=="töö")
asukohad %>%
filter(word=="armastus")
asukohad %>%
filter(word=="töö"|word=="armastus") %>%
ggplot(aes(x=asukoht,y=word))+
geom_point()
Nii võib otsida kõiksugu sõnu. Otsime näiteks sõnu kõrts ja kirik.
asukohad %>%
filter(word=="kõrts"|word=="kirik") %>%
ggplot(aes(x=asukoht,y=word))+
geom_point()
Eelmine kord sai lõpus mainitud, et kui lihtsa loendamisega me saame küll täpseid vorme leida, siis mõnikord tuleb teha otsinguid paindlikumalt. Selleks on võimalik kasutada regulaaravaldisi, millest oli ka kodutöö. Kasutada saame selleks filtrisse sobivat käsku str_detect(), ja uude tulpa sobivat käsku str_extract().
Kõige lihtsam regulaaravaldis on ilma erimärkideta, sel juhul me saame otsida sõnu, mis sisaldavad tähemärke seal sees. Nt otsides kõrtsi, saame kätte ka kõrtsmiku.
asukohad %>%
filter(str_detect(word,"kõrts")|str_detect(word,"kirik")) %>%
ggplot(aes(x=asukoht,y=word))+
geom_point()
asukohad %>%
filter(str_detect(word,"kõrts")|str_detect(word,"kirik")) %>%
mutate(type=str_extract(word,"kõrts|kirik")) %>%
ggplot(aes(x=asukoht,y=type))+
geom_point()
Proovi! Otsi mõnd sõna tekstist ja kuva nende asukohad samasuguses graafikus.
Üks huvitav teema võiks olla näiteks raha meie teoses, majapidamisest räägitakse palju ja tihti konkreetsete summadega. Leiame täitsa mitu vormi.
asukohad %>%
filter(str_detect(word,"rubla")) %>%
ggplot(aes(x=asukoht,y=word))+
geom_point()
Nüüd võib tekkida küsimus, et mis summadest ikkagi juttu on. Seni oleme vaadanud teksti sõnade kaupa. Nüüd aga tahaks teada midagi eelneva sõna kohta. Selleks on mitu võimalust.
Üks võimalus on võtta meie päringuvasted toortekstist välja. Seda saame teha str_extract() käsuga ja targa regulaaravaldisega. Proovime selle koostada.
rahad <- raamat1 %>%
mutate(leiud=str_extract_all(txt,"[a-zõäüöA-ZÕÄÖÜ]+ rubla"))
#Meil tekivad tabelisse loendid. Me saame neid lahutada käsuga unnest() valides kõigepealt ainult leidude tulba.
rahad %>%
select(leiud) %>%
unnest()
## Warning: `cols` is now required when using unnest().
## Please use `cols = c(leiud)`
Teine võimalus on koostada kohe alguses bigrammide loend. unnest_tokens() võimaldab seda teha mõne parameetri muutmisega ja teha ülevaatetabeleid ka sõnajärjenditest. Täpsemat infot saame vaadates dokumentatsiooni.
?unnest_tokens
Bigramme saame teha järgmiselt.
bigrammid <- raamat1 %>%
unnest_tokens(bigram, txt, token = "ngrams", n = 2, n_min = 2)
Nüüd võime rubla fraasid kätte saada puhtalt filter käsu abiga.
bigrammid %>%
filter(str_detect(bigram,"rubla"))
Me võime teha ka kolmgramme või viisgramme.
trigrammid <- raamat1 %>%
unnest_tokens(trigram, txt, token = "ngrams", n = 3, n_min = 3)
viisgrammid <- raamat1 %>%
unnest_tokens(fivegram, txt, token = "ngrams", n = 5, n_min = 5)
trigrammid %>%
count(trigram,sort=T)
viisgrammid %>%
count(fivegram,sort=T)
Kombinatsiooniülesanne! Lisa viisgrammidele asukoha märgendus ja kuva populaarsemate (nt top 10) viisgrammide asukohad tekstis. Vihjed: mutate(), row_number(), n(), filter().
Meil oli eelmine kord juttu sisu iseloomustamisest sõnade kaudu. Nagu me teame, kõige sagedasemad sõnad on üldiselt üpris sagedased kõigis eestikeelsetes tekstides. Sisuliselt huvitavad sõnad on tihti palju tagapool.
raamat1_sonaloend %>%
head(10)
raamat1_sonaloend %>%
slice(200:220)
Sisulisi sõnu võib leida korpuseid omavahel võrreldes, aga kõige lihtsam viis on lihtsalt jätta kõrvale sõnad, mis üldiselt sisu palju edasi ei anna. Sõnu, mis me eemaldame tekstist nimetatakse stopsõnadeks, nende jaoks on olemas enamasti standardnimekirjad. Eestikeelne nimekiri on meie andmetega kaasas. Loeme selle sisse.
stopwords <- read_csv("data/uiboaed_stopwords/estonian-stopwords.txt",col_names = F) %>% rename(word=X1)
## Parsed with column specification:
## cols(
## X1 = col_character()
## )
stopwords
Me saame siduda need sõnad oma andmetega join() käskude abil. Seekord kasutame varianti anti_join, kuna me tahame ühtivad read eemaldada.
raamat1_sonaloend %>%
anti_join(stopwords,"word")
Pärast seda ilmnevad juba sõnad, mis seda teost iseloomustavad. Antud juhul on selleks nimed. Kuna meie unnest_tokens() kaotas ära eristuse suurtähe ja väiketähe vahel, siis need nimed formaalselt enam sõnadest ei ei eristu. unnest_tokens() käsk on aga selle koha pealt paindlik. Vaadates unnest_tokens() dokumentatsiooni näeme aga, et seal on eraldi parameeter to_lower, mis on vaikimisi tõene. Muudame selle ära.
raamat1_sonad2 <- raamat1 %>%
unnest_tokens(word,txt,to_lower=F)
raamat1_sonad2 %>%
count(word,sort=T) %>%
anti_join(stopwords,"word")
Siin võime eemaldada kõik suurtähega sõnad. Selleks kasutame jälle regulaaravaldisi. Hüüumärgiga saab str_detect() käsu teha eitavaks. Otsime vormi, mis EI sisalda suurtähti.
raamat1_sonad2 %>%
count(word,sort=T) %>%
anti_join(stopwords,"word") %>%
filter(!str_detect(word,"[A-ZÕÄÖÜ]"))
Nii võime vaadata juba iga peatüki kohta sisulisi sõnu, mis seda iseloomustaks, eemaldades siis stopsõnad. Trükime välja igast peatükist esimesed kümme rida.
peatykid_sonad %>%
anti_join(stopwords,"word") %>%
mutate(row_number=row_number()) %>%
filter(row_number<11) %>%
filter(chapter<11) %>%
ggplot(aes(x=chapter,y=row_number,label=word))+
geom_label()
Võime ka siin nüüd jätta kõrvale suurtähtedega sõnad. Kas meil on tark suurtähed sisse jätta või mitte oleneb küsimusest. Muidu kui meil on sõna “ja” väikse ja suure tähega, siis loeb meie kood neid eraldi. Ja samuti on stopsõnade nimistu algselt ainult väikeste tähtedega. (Seda saab muidugi muuta.)
peatykid_sonad %>%
anti_join(stopwords,"word") %>%
filter(!str_detect(word,"[A-ZÕÄÖÜ]")) %>%
mutate(row_number=row_number()) %>%
filter(row_number<11) %>%
filter(chapter<11) %>%
ggplot(aes(x=chapter,y=row_number,label=word))+
geom_label()
LISAMATERJAL NÄIDISEKS: Tekstide sisu iseloomustamiseks on palju meetodeid. Üks teine viis selleni jõuda on otsida sõnu, mis iseloomustavad just seda tekstijuppi, aga mitte eriti teisi. Üks vahend, millele on mugav liides tehtud tidytextis on tf-idf (term frequency - inverse document frequency). Loe täpsemat sisu siit https://en.wikipedia.org/wiki/Tf-idf. Selle valemi aluseks on vaist, et kui sõna on sage ühes dokumendis, aga seda teistes tekstides tihti ei leidu, võiks seda pidada seda teksti eristavaks teistest ehk selle teksti märksõnaks. Selle arvutamiseks loetakse kokku kui palju on üht sõna konkreetses tekstis või tekstide kogumis ning korrutatakse läbi arvuga, mis iseloomustab kui paljudes tekstides seda on. Erinevaid variante sellest valemist on mitmeid ja see, milline neist on sobivaim konkreetse küsimuse lahendamiseks võib sõltuda korpuse suurusest, iseloomust või küsimusest endast. Me kasutame siin vaikevõimalust paketis.
tf_idfs_peatykid <- raamat1_sonad %>%
group_by(chapter) %>%
count(word,sort=T) %>%
bind_tf_idf(word,chapter,n) %>%
arrange(desc(tf_idf))
tf_idfs_peatykid %>%
mutate(row_number=row_number()) %>%
filter(row_number<11) %>%
filter(chapter<11) %>%
ggplot(aes(x=chapter,y=row_number,label=word))+
geom_label()
Proovi ka! Võta eristavad sõnad peatükid 10-20.
Samamoodi kui me vaatame eri peatükke teose sees võime vaadata ka eri teoseid.
Seekord loeme andmetena sisse rohkem kui ühe tekstifaili. Tekstid pärinevad ikka siit: https://datadoi.ee/handle/33/76. map_df() käsu pärast ei pea praegu muretsema. Käsk otsib üles failid kataloogist soned, seob nad omavahel ja siis teeb uue tulba filename.
filelist <- list.files("data/uiboaed_ilukirjandus/soned",full.names=T)
texts <- map_df(filelist, ~ tibble(txt = read_lines(.x)) %>%
mutate(filename = .x)) %>%
mutate(filename= basename(filename))
Sisu võime vaadata taas muutujat sisse kirjutades.
#texts
Seekord on meil materjale mitmest tekstist. Kasutades count() käsku saame kokku lugeda tekstiread, kus antud juhul oli igal real üks peatükk.
texts %>%
count(filename)
Paremaks ülevaateks võib selliseid asju kuvada.
texts %>%
count(filename) %>%
ggplot(aes(y=filename,x=n))+
geom_col()
Nummerdame peatükid ära.
texts <- texts %>%
group_by(filename) %>%
mutate(chapter=row_number()) %>%
ungroup()
Vaatame igas teoses teist ja kolmandat peatükki
#texts %>%
# filter(chapter==2|chapter==3)
# Selleks on mitmeid võimalusi.
#texts %>%
# filter(chapter %in% c(2,3))
Teeme tekstidest sõnad.
words <- texts %>%
unnest_tokens(word, txt)
Vaatame hetkeks andmestiku sisse.
words
Tuletame meelde, kuidas loendamine toimis. Loendage kokku, mitu sõna on igas failis. Ja kuva see samuti nagu peatükkide arvgi.
Loendame kokku ka mitu korda iga sõna esineb.
Kuvame ka need levinumad sõnad.
Kuidas saame sõnade esinemiskorrad iga teksti sees? Proovi!
Otsime mõnd konkreetset sõna tekstide sõnaloendis.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
filter(word=="peremees")
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
filter(word=="töö")
Kuidas saame kümme levinumat sõna ühes konkreetses tekstis? Proovi.
Kuidas saame kümme levinumat sõna igas tekstis? Proovi.
Nagu meil on varem juttu olnud, tasub tekstide võrdlemisel vaadata sagedust. Selleks jagame igas grupis leitu arvu grupisisese kogusummaga.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n))%>%
filter(row_number()<10) %>% #sort pani need juba õigessejärjekorda
ggplot(aes(y=word,x=freq,color=filename))+
geom_point()+
guides(color=F)
Kohanda! Vali välja üks sõna ning kuva selle sagedus eri tekstides.
Nagu öeldud on tihti meil vaja leida mitut seotud sõna. Võtame aluseks sagedustabeli ja otsime sealt ‘hobu’-ga algavat sõna.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n))
words %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
filter(str_detect(word,"^hobu"))
Me võime kõikide eri vormide esinemisarvud kokku liita ja kuvada neid.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
filter(str_detect(word,"^hobu")) %>%
ggplot(aes(y=filename,x=freq))+
geom_point()
Lisades veel korra count() funktsiooni, saame me vaadata ka mitmes tekstis see sõnavorm esineb sõna esineb.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
filter(str_detect(word,"^hobu")) %>%
ungroup() %>%
count(word,sort=T)
Esialgu on meil sõnad loendatud iga vormi kaupa. Need tuleb meil kokku liita. Selleks on olemas käsk summarise(), mis käitub samamoodi kui mutate(), aga kaotab ära korduvad read ja ebavajalikud tulbad.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
filter(str_detect(word,"^hobu")) %>%
summarise(freq=sum(freq)) %>% # tekstid on grupeeritud failinime kaupa
ggplot(aes(y=filename,x=freq))+
geom_point()
## `summarise()` ungrouping output (override with `.groups` argument)
Moodusta üks otsing regulaaravaldisega ja kujuta selle tulemust. (Märkus: tabelis on hetkel iga sõna eraldi real, seega ei saa regulaaravaldisega otsida siin kahesõnalisi ühendeid.)
Nüüd vaatasime kuidas regulaaravaldisi sai kasutada andmete filtreerimiseks. Neis saab kasutada ka uute muutujate tegemiseks. Kui str_detect() andist TÕENE või VÄÄR vastuse sõnaridade kohta, siis str_extract() võtab uude muutujasse välja kattuva sõnaosa.
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
filter(str_detect(word,"^kirik")|str_detect(word,"^kõrts")) %>%
ggplot(aes(y=filename,x=freq))+
geom_point()
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
ungroup() %>%
mutate(searchterm=str_extract(word,"^kirik|^kõrts")) %>%
#filter(searchterm=="kirik"|searchterm=="kõrts")+
filter(!is.na(searchterm)) %>% # kasutame ainult ridu, kus on olemas info searchterm tulbas
group_by(filename,searchterm) %>%
summarise(freq=sum(freq)) %>% # tekstid on grupeeritud failinime kaupa
ggplot(aes(y=filename,x=freq,color=searchterm))+
geom_point(alpha=0.5)
## `summarise()` regrouping output by 'filename' (override with `.groups` argument)
Moodusta 2-3 otsingut ja kuva nende sagedusi erinevates raamatutes.
Kitsenda oma sõnaotsingut, nii et me otsime ainult ühe autori teoseid. (Vihje: filter() või str_detect().
Näidis ühe teose kohta
words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
mutate(searchterm=str_extract(word,"^kirik|^kõrts")) %>%
group_by(filename,searchterm) %>%
filter(!is.na(searchterm)) %>%
summarise(freq=sum(freq)) %>%
filter(str_detect(filename,"oigus"))
## `summarise()` regrouping output by 'filename' (override with `.groups` argument)
Eemaldame raamatuist stopsõnad. Proovi ise!
words %>%
anti_join(stopwords,"word")
Loeme kokku iga teose levinumad sõnad kui stopsõnad on kõrvale jäetud. Vaatame esimest 100 sõna lähemalt.
freqwords <- words %>%
anti_join(stopwords,"word") %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
arrange(desc(freq)) %>%
head(100)
words %>%
count(word,sort=T) %>%
filter(str_detect(word,"w"))
Nüüd on tipus nimed, aga ka mõned levinud sõnad kirjutatuna w-ga. Stopsõnadega ühendamine ei toiminud siin kuna ‘weel’ ja ‘wõi’ ei olnud nimekirjas. Sellisel juhul tasub mõnikord sisendit käsitsi parandada. Selleks saame kasutada veel üht funktsiooni str_replace_all(), mis asendab ühe sõnajada teisega. Siin saab kasutada ka regulaaravaldis.
freqwords <- words %>%
mutate(word=str_replace_all(word,"w","v")) %>%
anti_join(stopwords,"word") %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
arrange(desc(freq)) %>%
head(100)
Samamoodi nagu varem võime jätta kõrvale ka suurtähega algavad sõnad. Selleks peame me muutma ühestaja parameetreid.
freqwords <- texts %>%
unnest_tokens(word,txt,to_lower=F) %>%
mutate(word=str_replace_all(word,"w","v")) %>%
anti_join(stopwords,"word") %>%
group_by(filename) %>%
filter(!str_detect(word,"[A-ZÕÄÖÜ]")) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
arrange(desc(freq)) %>%
head(100)
LISAMATERJAL NÄIDISEKS: Sisulise ülevaate saamiseks võime ka arvutada välja sõnad, mis raamatuid teineteisest eristavad. Ilukirjandusteoseid omavahel võrreldes saab päris hea ülevaate nende sisust, sellest mis on igale teosele eriomane.
keywords <- texts %>%
unnest_tokens(word,txt,to_lower=F) %>%
mutate(word=str_replace_all(word,"w","v")) %>%
#anti_join(stopwords,"word") %>%
group_by(filename) %>%
filter(!str_detect(word,"[A-ZÕÄÖÜ]")) %>%
count(word,sort=T) %>%
bind_tf_idf(word,filename,n) %>%
arrange(desc(tf_idf)) %>%
head(100)
Proovi ise! Vali välja üks teos ja väljasta selle 10 eristavat sõna, jättes kõrvale suurtähte sisaldavad sõnad.
Kui me saame tulemused, mida me soovime esitada, kas hiljem või väljaspool RStudiot, siis peaksime need ka salvestama. Selleks on olemas tabelite puhul read_ käskude asemel write_ käsud. ggplot2 graafikuid saab salvestada funktsiooniga ggsave().
tabel <- words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
mutate(searchterm=str_extract(word,"^mets|^maa|^linn")) %>%
filter(!is.na(searchterm)) %>% # kasutame ainult ridu, kus on olemas info searchterm tulbas
group_by(filename,searchterm) %>%
summarise(freq=sum(freq))
## `summarise()` regrouping output by 'filename' (override with `.groups` argument)
tabel %>%
write_tsv("data/mets_maa_linn.tsv")
graafik <- words %>%
group_by(filename) %>%
count(word,sort=T) %>%
mutate(freq=n/sum(n)) %>%
mutate(searchterm=str_extract(word,"^mets|^maa|^linn")) %>%
filter(!is.na(searchterm)) %>% # kasutame ainult ridu, kus on olemas info searchterm tulbas
group_by(filename,searchterm) %>%
summarise(freq=sum(freq)) %>% # tekstid on grupeeritud failinime kaupa
ggplot(aes(y=filename,x=freq,color=searchterm))+
geom_point(alpha=0.5)
## `summarise()` regrouping output by 'filename' (override with `.groups` argument)
ggsave("figures/mets_maa_linn.png",graafik)
## Saving 7 x 5 in image
Siin on mõned harjutused tehtu kinnistamiseks. Proovi ära teha vähemalt 4 7-st ülesandest.
Vali välja üks teos tänasest komplektist. Loe see fail sisse ja nummerda peatükid.
Tee tekstiväljadest sõnaväljad, salvesta iga sõna juures asukohainfo.
Vali välja kolm sõna ning kuva nende asukohad teoses
Arvuta välja nende kolme sõna sagedused igas peatükis.
Otsi kolme terminit regulaaravaldiste abil nii, et eri käändevormid ja tuletised satuksid samale kujule. Kuva nende asukohad tekstis.
Loe kokku vastete sagedused tekstis.
Võta kokku 10 kõige sagedasemat sõna ning esita nende sagedused kolmes vabalt valitud teoses.